//===--- GenMeta.h - Swift IR generation for metadata -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file provides the private interface to the metadata emission code.
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_IRGEN_INTERNAL_GENMETA_H
#define POLARPHP_IRGEN_INTERNAL_GENMETA_H

#include <utility>

namespace llvm {
template <class T> class ArrayRef;
class Constant;
class Function;
class GlobalVariable;
class Value;
}

namespace polar {

class AbstractFunctionDecl;
class FileUnit;
class FuncDecl;
enum class ResilienceExpansion : unsigned;
class PILType;
class VarDecl;
enum class SpecialInterface : uint8_t;

namespace irgen {

class ConstantStructBuilder;
class FieldTypeInfo;
class GenericTypeRequirements;
class IRGenFunction;
class IRGenModule;
enum RequireMetadata_t : bool;
class Size;
class StructLayout;
class ClassLayout;

bool requiresForeignTypeMetadata(CanType type);
bool requiresForeignTypeMetadata(NominalTypeDecl *decl);

/// Emit the metadata associated with the given class declaration.
void emitClassMetadata(IRGenModule &IGM, ClassDecl *theClass,
                       const ClassLayout &fragileLayout,
                       const ClassLayout &resilientLayout);

/// Emit the constant initializer of the type metadata candidate for
/// the given foreign class declaration.
llvm::Constant *emitForeignTypeMetadataInitializer(IRGenModule &IGM,
                                                   CanType type,
                                                   Size &addressPointOffset);

/// Emit a type context descriptor that was demanded by a reference from
/// other generated definitions.
void emitLazyTypeContextDescriptor(IRGenModule &IGM,
                                   NominalTypeDecl *type,
                                   RequireMetadata_t requireMetadata);

/// Emit type metadata that was demanded by a reference from other
/// generated definitions.
void emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type);


/// Emit metadata for a foreign struct, enum or class.
void emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl);

/// Emit the metadata associated with the given struct declaration.
void emitStructMetadata(IRGenModule &IGM, StructDecl *theStruct);

/// Emit the metadata associated with the given enum declaration.
void emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum);

/// Get what will be the index into the generic type argument array at the end
/// of a nominal type's metadata.
int32_t getIndexOfGenericArgument(IRGenModule &IGM,
                                  NominalTypeDecl *decl,
                                  ArchetypeType *archetype);

/// Given a reference to nominal type metadata of the given type,
/// derive a reference to the type metadata stored in the nth
/// requirement slot.  The type must have generic arguments.
llvm::Value *emitArgumentMetadataRef(IRGenFunction &IGF,
                                     NominalTypeDecl *theDecl,
                                     const GenericTypeRequirements &reqts,
                                     unsigned reqtIndex,
                                     llvm::Value *metadata);

/// Given a reference to nominal type metadata of the given type,
/// derive a reference to a protocol witness table stored in the nth
/// requirement slot.  The type must have generic arguments.
llvm::Value *emitArgumentWitnessTableRef(IRGenFunction &IGF,
                                         NominalTypeDecl *theDecl,
                                         const GenericTypeRequirements &reqts,
                                         unsigned reqtIndex,
                                         llvm::Value *metadata);

/// Given a metatype value, read its instance type.
llvm::Value *emitMetatypeInstanceType(IRGenFunction &IGF,
                                      llvm::Value *metatypeMetadata);

/// Emit the field type accessor for a nominal type's metadata. This function
/// lazily generates the metadata for the types of all of the nominal type's
/// fields for reflection purposes.
void emitFieldTypeAccessor(IRGenModule &IGM,
                           NominalTypeDecl *type,
                           llvm::Function *fn,
                           ArrayRef<FieldTypeInfo> fieldTypes);

/// Adjustment indices for the address points of various metadata.
/// Size is in words.
namespace MetadataAdjustmentIndex {
enum : unsigned {
   // Class metadata has two words of head-allocated data: the destructor
   // and the value witness table.
      Class = 2,

   // Struct and enum metadata have one word of head-allocated data:
   // the value witness table.
      ValueType = 1,

   // Other metadata objects have no head allocation.
      None = 0,
};
}

/// Get the runtime identifier for a special protocol, if any.
SpecialInterface getSpecialInterfaceID(InterfaceDecl *P);

/// Use the argument as the 'self' type metadata.
void getArgAsLocalSelfTypeMetadata(IRGenFunction &IGF, llvm::Value *arg,
                                   CanType abstractType);

/// Description of the metadata emitted by adding generic requirements.
struct GenericRequirementsMetadata {
   unsigned NumRequirements = 0;
   unsigned NumGenericKeyArguments = 0;
   unsigned NumGenericExtraArguments = 0;
};

/// Add generic requirements to the given constant struct builder.
///
/// \param sig The generic signature against which the requirements are
/// described.
///
/// \param requirements The requirements to add.
GenericRequirementsMetadata addGenericRequirements(
   IRGenModule &IGM,
   ConstantStructBuilder &B,
   GenericSignature sig,
   ArrayRef<Requirement> requirements);

} // end namespace irgen
} // end namespace polar

#endif
